package com.cd826dong.clouddemo;

import com.cd826dong.clouddemo.filter.HystrixRequestContextServletFilter;
import com.cd826dong.clouddemo.filter.JWTOAuthTokenInterceptor;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cloud.bus.jackson.RemoteApplicationEventScan;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.web.client.RestTemplate;

import java.util.Collections;
import java.util.List;

/*@EnableBinding注解就可
以让Spring Cloud Stream启用一个消息代理， 但这里需要绑定到是Sink
        接口所定义的消息通道（input） ， 而不是Source接口。 Sink接口是一
        个消息订阅接口， 通过该注解， 后续就可以使用该通道接收用户变更
        消息， 然后进行相关的处理*/
@EnableBinding(Sink.class)
//开启对服务的容错保护,hystrix
@EnableCircuitBreaker
@EnableEurekaClient
//@EnableFeignClients注解， 用 来为应用开启Feign相关功能
@EnableFeignClients
@RemoteApplicationEventScan
@EnableResourceServer
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    // 增加@LoadBalanced， 也就是让restTemplate具有负载均衡功能
    /**该注解由Ribbon提供， 通过该注解， Ribbon
    为Rest客户端（RestTemplate） 的实现增加了客户端负载均衡功能， 其
    实现原理可以概括为下面4个步骤：
            （1） Ribbon首先根据其所在Zone优先选择一个负载较少的Eureka
    服务器。
            （2） 定期从Eureka服务器更新， 并过滤服务实例列表。
            （3） 根据指定的负载均衡策略， 从可用的服务实例列表中选择
    一个。
            （4） 然后使用该地址， 通过Rest客户端进行服务调用。
    默认情况下， 客户端负载均衡采用了轮询策略
    （RoundRobinRule） ， 也就是说Ribbon会交替访问从Eureka中所获取
            的用户微服务实例*/
    @Primary
    @LoadBalanced
    @Bean(value = "loadBalancedRestTemplate")
    RestTemplate loadBalancedRestTemplate(){
        return new RestTemplate();
    }


    @Bean(value = "restTemplate")
    RestTemplate restTemplate(){
        return new RestTemplate();
    }


    @Bean
    public OAuth2RestTemplate oAuth2RestTemplate(OAuth2ClientContext oAuth2ClientContext,
                                                 OAuth2ProtectedResourceDetails details){
        return new OAuth2RestTemplate(details, oAuth2ClientContext);
    }
    
    @Bean
    public FilterRegistrationBean indexFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean(new HystrixRequestContextServletFilter());
        registration.addUrlPatterns("/*");
        return registration;
    }
    
    @Bean(value = "jwtRestTemplate")
    @Primary
    public RestTemplate getCustomRestTemplate() {
        RestTemplate template = new RestTemplate();
        List interceptors = template.getInterceptors();
        if (interceptors == null) {
            template.setInterceptors(Collections.singletonList(new JWTOAuthTokenInterceptor()));
        } else {
            interceptors.add(new JWTOAuthTokenInterceptor());
            template.setInterceptors(interceptors);
        }
        return template;
    }


}
